package puzzle.nurikabe.data;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import puzzle.common.data.HatanException;
import puzzle.common.point.Pnt;

public class ShimaState {
	public int i;
	public int n;
	public Pnt p;
	public Pnt lefttop;
	public Pnt rightbottom;
	private int m_area;
	private boolean m_isFixed;
	private ShimaPotential m_potential;
	
	private boolean m_isDirty;
	private boolean m_isDirtyBuf;

	public ShimaState(int i, int n, int x, int y, int width, int height) {
		this.i = i;
		this.n = n;
		this.p = new Pnt(x, y);
		m_area = 1;
		m_isFixed = false;

		int x0 = Math.max(0, x - n + 1);
		int y0 = Math.max(0, y - n + 1);
		int x1 = Math.min(width - 1, x + n - 1);
		int y1 = Math.min(height - 1, y + n - 1);

		this.lefttop = new Pnt(x0, y0);
		this.rightbottom = new Pnt(x1, y1);
		m_potential = new ShimaPotential(n, x0 - x, y0 - y, x1 - x, y1 - y);
		
		m_isDirty = true;
		m_isDirtyBuf = false;
	}

	public ShimaState(ShimaState other) {
		this.i = other.i;
		this.n = other.n;
		this.p = other.p;
		this.lefttop = other.lefttop;
		this.rightbottom = other.rightbottom;
		m_area = other.m_area;
		m_isFixed = other.m_isFixed;
		m_potential = other.m_potential.dup();
		
		m_isDirty = other.m_isDirty;
		m_isDirtyBuf = other.m_isDirtyBuf;
	}

	public ShimaState dup() {
		return new ShimaState(this);
	}

	public void incrementArea() throws HatanException {
		if (m_area == n) {
			throw new HatanException();
		}
		m_area++;
	}

	public int getArea() {
		return m_area;
	}

	public int getRequiredAdditionalArea() {
		return n - m_area;
	}

	public boolean isFixed() {
		return m_isFixed;
	}

	public boolean setFixed() {
		if (m_isFixed) {
			return false;
		} else {
			m_isFixed = true;
			return true;
		}
	}
	
	public boolean setForbidden(int x, int y) {
		boolean isChanged = m_potential.updatePotential(x - p.x, y - p.y, 0);
		if (isChanged) setDirty();
		return isChanged;
	}

	public int getPotential(int x, int y) {
		return m_potential.getPotential(x - p.x, y - p.y);
	}

	public int getPossibleArea() {
		return m_potential.getPossibleArea();
	}

	public List<Pnt> getPossiblePoints() {
		List<Pnt> result = new ArrayList<Pnt>();
		for (Collection<Pnt> cells : m_potential.getCellsPerPotential()) {
			for (Pnt p : cells) {
				result.add(new Pnt(p.x + this.p.x, p.y + this.p.y));
			}
		}
		return result;
	}
	
	public boolean setForbiddenAround(int x, int y) {
		boolean isChanged = false;
		isChanged |= setForbidden(x, y);
		isChanged |= setForbidden(x, y - 1);
		isChanged |= setForbidden(x + 1, y);
		isChanged |= setForbidden(x, y + 1);
		isChanged |= setForbidden(x - 1, y);
		return isChanged;
	}

	public boolean confirm(int x, int y) throws HatanException {
		int n = getPotential(x, y);
		if (n == 0) {
			throw new HatanException();
		}

		if (isDebug()) {
			System.out.println("Confirming " + (x - p.x) + "," + (y - p.y) + " on:");
			System.out.println(m_potential);
		}

		boolean isChanged = m_potential.confirm(x - p.x, y - p.y);

		if (isDebug()) {
			if (isChanged) {
				System.out.println("Result:");
				System.out.println(m_potential);
			} else {
				System.out.println("Result: no change");
			}
		}
		if (isChanged) setDirty();
		return isChanged;
	}

	public int getNumOfCellsPerPotential(int potential) {
		return m_potential.getNumOfCellsPerPotential(potential);
	}

	private boolean isDebug() {
		return false;
	}

	private void setDirty() {
		m_isDirtyBuf = true;
	}
	
	public void snapshotDirtyFlag() {
		m_isDirty = m_isDirtyBuf;
		m_isDirtyBuf = false;
	}
	
	public boolean isDirty() {
		return m_isDirty;
	}

}
